#!/usr/bin/env python
import os
import re
import sys
import xml.dom.minidom
import string
from os.path import exists

from numpy import *
from optparse import *

class SvgTransform(object):
    def __init__(self):
        pass
    
    def transform(self,points):
        pass

class SvgMatrixTransform(SvgTransform):
    def __init__(self, a, b, c, d, e, f):
        SvgTransform.__init__(self)
        self.matrix = matrix([[a,c,e],[b,d,f],[0,0,1]])

    def transform(self,points):
        return [(self.matrix * matrix([[pset[0]], [pset[1]], [1]])).transpose().tolist()[0][0:2] for pset in points]

class SvgTranslateTransform(SvgTransform):
    def __init__(self, tx, ty):
        SvgTransform.__init__(self)
        self.tx = tx
        self.ty = ty

    def transform(self,points):
        return [[pset[0] + self.tx, pset[1] + self.ty] for pset in points]

class SvgScaleTransform(SvgTransform):
    def __init__(self, sw, sh):
        SvgTransform.__init__(self)
        self.sw = sw
        self.sh = sh

    def transform(self,points):
        return [[pset[0] * self.sw, pset[1] * self.sh] for pset in points]

def parsePolylinePoints(pointStr):
    return [[float(a[0]),float(a[1])] for a in [pair.split(',') for pair in pointStr.split(' ')]]
    
def parsePoints(pointsStr):
    return [map(float, pSet.strip().split(',')) for pSet in pointsStr.split()]
    
def parsePathPoints(pathDef):
    points = []
    pointset = None
    for command in re.findall(r'([MLHVCSQTAZmlhvcsqtaz])([^MLHVCSQTAZmlhvcsqtaz]+)?', pathDef):

        if not pointset:
            pointset = []

        if command[0] in ('M','L') :
            pointset.extend(parsePoints(command[1]))
        elif command[0] == 'z':
            #pointset.append(list(pointset[0]))
            points.append(pointset[:])
            pointset = None
        else:
            print('Unsupported path command: {0}'.format(command[0]))
            continue
    
    if pointset and len(pointset):
        points.append(pointset)
    
    return points

def parseTransform(transformStr):
    transforms = []
    for transform in re.findall(r'(\w+)\(([^\)]*)\)', transformStr):
        transformArgs = [float(a.strip()) for a in transform[1].split(',')]
        if transform[0] == 'translate':
            transforms.append(SvgTranslateTransform(*transformArgs)) 
        elif transform[0] == 'scale':
            transforms.append(SvgScaleTransform(*transformArgs)) 
        elif transform[0] == 'matrix':
            transforms.append(SvgMatrixTransform(*transformArgs)) 
        else:
            print('Unsupported transform type: {0}'.format(transform[0]))
            continue
    return transforms

def getSvgShapes(svgElem, transforms, skipIds=None, verbose=False):
    shapes = {}

    # SVG shape elements: path, rect, circle, ellipse, line, polyline and polygon.
    for e in svgElem.childNodes:
        if e.nodeType == e.ELEMENT_NODE:
            paths = []
            nodeName = e.nodeName
            if e.hasAttribute('id'):
                eId = e.getAttribute('id')
                if skipIds and eId in skipIds:
                    if verbose:
                        print('Skipping {0}'.format(eId))
                    continue
            
            if nodeName == 'g':
                if verbose:
                    print('Parsing group {0}'.format(eId))
        
                groupTransforms = []
                if e.hasAttribute('transform'):
                    groupTransforms = parseTransform(e.getAttribute('transform'))
    
                shapes.update(getSvgShapes(e, groupTransforms + transforms, skipIds, verbose))
            else:
                if nodeName == 'path':
                    points = parsePathPoints(e.getAttribute('d'))
                    for pointset in points:
                        paths.append([eId, pointset])
                elif nodeName == 'polyline':
                    pointset = parsePolylinePoints(e.getAttribute('points'))
                    paths.append([eId, pointset])
                elif nodeName in ('rect', 'circle', 'ellipse', 'line', 'polygon'):
                    print('Unsupported shape element: {0}'.format(nodeName))
                    continue
                else:
                    continue
                
                elementTransforms = []
                if e.hasAttribute('transform'):
                    elementTransforms = parseTransform(e.getAttribute('transform'))
        
                if verbose:
                    print('Parsed {0} {1}'.format(nodeName, eId))
        
                transformStack = elementTransforms + transforms 
                if len(transformStack):
                    for path in paths:
                        for t in transformStack:
                            if verbose:
                                print('Applying transform {0}'.format(t))
                            path[1] = t.transform(path[1])
                
                shapes[eId] = paths
    
    return shapes

def parseArgs():
    parser = OptionParser(usage='%prog [options] filename')
    parser.add_option('--groups', help='Limit exported paths to thoughs contained in the comma delimited list of groups')
    parser.add_option('--width', type='float', default=0.0, help='Width of target image')
    parser.add_option('--height', type='float', default=0.0, help='Height of target image')
    parser.add_option('--verbose', action='store_true', default=False, help='Verbose console output')
    parser.add_option('--skip', type='string', help='Comma delimited list of element IDs to skip when processing')
    (options,args) = parser.parse_args()
    
    if len(args) == 0:
        parser.error("missing filename argument")
    if not exists(args[0]):
        parser.error("Input file {0} does not exist".format(args[0]))
    
    return (options,args)

def makeImap():
    (options,args) = parseArgs()

    if options.groups:
        groups = options.groups.split(',')

    if options.skip:
        skipIds = options.skip.split(',')

    fname = args[0]

    svgDom = xml.dom.minidom.parse(fname)
    svgElem = svgDom.getElementsByTagName('svg')[0]

    globalTransforms = []
    
    viewportWidth = float(svgElem.getAttribute('width'))
    viewportHeight = float(svgElem.getAttribute('height'))
    
    if svgElem.hasAttribute('viewBox'):
        if options.width:
            viewportWidth = options.width
        
        if options.height:
            viewportHeight = options.height
        
        viewboxAttr = svgElem.getAttribute('viewBox')
        (vbX, vbY, vbW, vbH) = [float(n) for n in re.split('(?:,|\s)*', viewboxAttr)]
        
        if( vbX != 0 or vbY != 0 ):
            globalTransforms.append(SvgTranslateTransform(vbX,vbY))
            
        if( vbW != viewportWidth or vbH != viewportHeight ):
            globalTransforms.append(SvgScaleTransform(viewportWidth/vbW,viewportHeight/vbH))

    elif options.width or options.height:
        scaleW = options.width/viewportWidth if options.width else 1
        scaleH = options.height/viewportHeight if options.height else 1
        globalTransforms.append(SvgScaleTransform(scaleW,scaleH))

    shapes = getSvgShapes(svgElem, globalTransforms, skipIds=skipIds, verbose=options.verbose) 
    areas = []
    for id in shapes:
        for path in shapes[id]:
            areas.append("<area href=\"javascript:area_clicked()\" title=\"{0}\" shape=\"poly\" coords=\"{1}\" />".format(path[0], ', '.join([("%d,%d" % (p[0], p[1])) for p in path[1]])))
    
    html = string.Template('''<html>
<head>
    <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'></script>
    <script src='jquery.maphilight.js'></script>
    <script>
        function area_clicked(ev){
            alert("Clicked");
        }
        
        $(window).load(function(){
            $('img[usemap]').maphilight();
        });
    </script>
</head>
<body>
    <img src="$image" usemap="#imap">
    <map name="imap" id="imap">
$areas
    </map>
</body>
</html>''').safe_substitute(image=args[0].replace('.svg', '.png'),areas='\n\t\t'.join(areas))
    
    outfile = open(args[0].replace('.svg', '.html'), 'w')
    outfile.write(html)

if __name__ == '__main__':
    makeImap()